package com.imooc.pan.server.modules.file.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.imooc.pan.core.constants.RPanConstants;
import com.imooc.pan.core.exception.RPanBusinessException;
import com.imooc.pan.core.utils.FileUtil;
import com.imooc.pan.core.utils.IdUtil;
import com.imooc.pan.server.common.event.file.DeleteFileEvent;
import com.imooc.pan.server.common.event.search.UserSearchEvent;
import com.imooc.pan.server.common.utils.HttpUtil;
import com.imooc.pan.server.modules.file.constants.FileConstants;
import com.imooc.pan.server.modules.file.context.*;
import com.imooc.pan.server.modules.file.converter.FileConverter;
import com.imooc.pan.server.modules.file.entity.RPanFile;
import com.imooc.pan.server.modules.file.entity.RPanFileChunk;
import com.imooc.pan.server.modules.file.entity.RPanUserFile;
import com.imooc.pan.server.modules.file.enums.DelFlagEnum;
import com.imooc.pan.server.modules.file.enums.FileTypeEnum;
import com.imooc.pan.server.modules.file.enums.FolderFlagEnum;
import com.imooc.pan.server.modules.file.service.RPanFileChunkService;
import com.imooc.pan.server.modules.file.service.RPanFileService;
import com.imooc.pan.server.modules.file.service.RPanUserFileService;
import com.imooc.pan.server.modules.file.mapper.RPanUserFileMapper;
import com.imooc.pan.server.modules.file.vo.*;
import com.imooc.pan.storage.engine.core.StorageEngine;
import com.imooc.pan.storage.engine.core.context.ReadFileContext;
import org.apache.commons.lang3.StringUtils;
import org.assertj.core.util.Lists;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.NoSuchMessageException;
import org.springframework.core.ResolvableType;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.annotation.Annotation;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;

/**
* @author 黄怀镔
* @description 针对表【r_pan_user_file(用户文件信息表)】的数据库操作Service实现
* @createDate 2024-06-06 22:44:17
*/
@Service
public class RPanUserFileServiceImpl extends ServiceImpl<RPanUserFileMapper, RPanUserFile> implements RPanUserFileService, ApplicationContextAware {


    private ApplicationContext applicationContext;

    @Autowired
    private RPanFileService rPanFileService;

    @Autowired
    private FileConverter fileConverter;

    @Autowired
    private RPanFileChunkService rPanFileChunkService;

    @Autowired
    private StorageEngine storageEngine;




    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    /**
     * 创建文件夹
     * @param createFolderContext
     * @return
     */
    @Override
    public Long createFolder(CreateFolderContext createFolderContext) {
        return saveUserFile(createFolderContext.getParentId(),
                createFolderContext.getFolderName(),
                FolderFlagEnum.YES,
                null,
                null,
                createFolderContext.getUserId(),
                null);
    }

    @Override
    public RPanUserFile getUserRootFile(Long userId) {
        return lambdaQuery()
                .eq(RPanUserFile::getUserId,userId)
                .eq(RPanUserFile::getParentId,FileConstants.TOP_PARENT_ID)
                .eq(RPanUserFile::getFolderFlag,FolderFlagEnum.YES.getCode())
                .eq(RPanUserFile::getDelFlag,DelFlagEnum.NO.getCode())
                .one();
    }

    @Override
    public List<RPanUserFileVo> queryFileList(QueryFileContext fileContext) {
        return baseMapper.selectFileList(fileContext);
    }

    /**
     * 修改文件名称
     * @param updateFilenameContext
     */
    @Override
    public void updateFilename(UpdateFilenameContext updateFilenameContext) {
        checkUpdateFilename(updateFilenameContext);
        doUpdateFilename(updateFilenameContext);
    }

    /**
     * 批量删除文件
     * @param deleteFileContext
     */
    @Override
    public void deleteFile(DeleteFileContext deleteFileContext) {
        checkDeleteFile(deleteFileContext);
        doDeleteFile(deleteFileContext);
        afterDeleteFile(deleteFileContext);
    }

    @Override
    public boolean secUpload(SecUploadFileContext secUploadFileContext) {
        RPanFile record = getRecordByUserIdAndIdentifier(secUploadFileContext.getUserId(),secUploadFileContext.getIdentifier());
        if(Objects.isNull(record)){
            return false;
        }
        saveUserFile(secUploadFileContext.getParentId(),
                secUploadFileContext.getFileName(),
                FolderFlagEnum.NO,
                FileTypeEnum.getFileType(FileUtil.getFileSuffix(secUploadFileContext.getFileName())),
                record.getFileId(),
                secUploadFileContext.getUserId(),
                record.getFileSizeDesc());
        return true;
    }

    /**
     * 文件上传
     * 保存文件信息
     * 保存用户文件关系记录
     * @param fileContext
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void upload(FileUploadContext fileContext) {
        saveFile(fileContext);
        saveUserFile(fileContext.getParentId(),
                fileContext.getFilename(),
                FolderFlagEnum.NO,
                FileTypeEnum.getFileType(FileUtil.getFileSuffix(fileContext.getFilename())),
                fileContext.getRecord().getFileId(),
                fileContext.getUserId(),
                fileContext.getRecord().getFileSizeDesc());
    }

    /**
     * 上传实体文件
     * 保存文件分片记录
     * 校验是否全部分片上传成功
     * @param fileChunkUploadContext
     * @return
     */
    @Override
    public FileChunkUploadVo chunkUpload(FileChunkUploadContext fileChunkUploadContext) {
        FileChunkSaveContext fileChunkSaveContext = fileConverter.fileChunkUploadContextToFileChunkSaveContext(fileChunkUploadContext);
        rPanFileChunkService.saveFileChunk(fileChunkSaveContext);
        FileChunkUploadVo vo = new FileChunkUploadVo();
        vo.setMergeFlag(fileChunkSaveContext.getMergeFlagEnum().getCode());
        return vo;
    }

    /**
     * 查询用户已上传的分片列表
     * @param context
     * @return
     */
    @Override
    public UploadChunksVO getUploadChunksVO(QueryUploadChunksContext context) {
        LambdaQueryWrapper<RPanFileChunk> lqw = new LambdaQueryWrapper<>();
        lqw.eq(RPanFileChunk::getCreateUser,context.getUserId())
                .eq(RPanFileChunk::getIdentifier,context.getIdentifier())
                .gt(RPanFileChunk::getExpirationTime,new Date());
        List<RPanFileChunk> list = rPanFileChunkService.list(lqw);
        List<Integer> uploadChunks = list.stream().map(RPanFileChunk::getChunkNumber).collect(Collectors.toList());
        UploadChunksVO vo = new UploadChunksVO();
        vo.setUploadChunks(uploadChunks);
        return vo;
    }

    /**
     * 文件分片合并
     *
     * 保存文件实体记录
     * 保存文件用户映射
     * @param context
     */
    @Override
    public void mergeFile(FileChunkMergeContext context) {
        //文件物理合并和保存文件实体记录
        mergeFileChunkAndSaveFile(context);
        //保存文件用户映射
        saveUserFile(context.getParentId(),
                context.getFilename(),
                FolderFlagEnum.NO,
                FileTypeEnum.getFileType(FileUtil.getFileSuffix(context.getFilename())),
                context.getRecord().getFileId(),
                context.getUserId(),
                context.getRecord().getFileSizeDesc());
    }

    /**
     * 文件下载
     * @param context
     */
    @Override
    public void download(FileDownloadContext context) {
        RPanUserFile record = getById(context.getFileId());
        checkOperatePermission(record,context.getUserId());
        if(CheckFolder(record)){
            throw new RPanBusinessException("文件夹暂不支持下载");
        }
        doDownloadFile(record,context.getHttpServletResponse());
    }

    /**
     * 文件预览
     * @param context
     */
    @Override
    public void preview(FilePreviewContext context) {
        RPanUserFile record = getById(context.getFileId());
        checkOperatePermission(record,context.getUserId());
        if(CheckFolder(record)){
            throw new RPanBusinessException("文件夹暂不支持预览");
        }
        doPreviewFile(record,context.getHttpServletResponse());
    }

    @Override
    public List<FolderTreeNodeVO> queryFolderTree(QueryFolderTreeContext context) {
        Long userId = context.getUserId();
        List<RPanUserFile> record = lambdaQuery()
                .eq(RPanUserFile::getUserId, userId)
                .eq(RPanUserFile::getFolderFlag, FolderFlagEnum.YES.getCode())
                .eq(RPanUserFile::getDelFlag,DelFlagEnum.NO.getCode())
                .list();
        if(CollectionUtils.isEmpty(record)){
            return Lists.newArrayList();
        }
        List<FolderTreeNodeVO> result = assembFolderTreeNodeVOList(record);
        return result;
    }

    /**
     * 移动文件
     * @param context
     */
    @Override
    public void transfer(TransferFileContext context) {
        checkTransferCondition(context);
        doTransfer(context);
    }

    /**
     * 文件复制
     * @param context
     */
    @Override
    public void copy(CopyFileContext context) {
        checkCopyCondition(context);
        doCopy(context);
    }

    /**
     * 文件搜索
     * @param context
     * @return
     */
    @Override
    public List<FileSearchResultVO> search(FileSearchContext context) {
        List<RPanUserFile> records = getFileSearchResult(context);
        Set<Long> parentIds = records.stream().map(RPanUserFile::getParentId).collect(Collectors.toSet());
        List<RPanUserFile> parentRecords = listByIds(parentIds);
        //映射map，key为parentId，value为filename
        Map<Long, String> parentMap = parentRecords.stream().collect(Collectors.toMap(k -> k.getFileId(), v -> v.getFilename()));
        List<FileSearchResultVO> result = records.stream().map(fileConverter::rPanUserFileToFileSearchResultVO).collect(Collectors.toList());
        result.stream().forEach(record->{
            if(record.getParentId() == 0L){
                record.setParentFilename(FileConstants.ALL_FILE_CN_STR);
            }
            String parentFilename = parentMap.get(record.getParentId());
            record.setParentFilename(parentFilename);
        });
        //搜索之后
        afterSearch(context);
        return result;
    }

    @Override
    public List<BreadCurmbVO> getBreadcurmbs(QueryBreadCurmsContext context) {
        List<RPanUserFile> folderRecords = queryFolderList(context.getUserId());
        if(CollectionUtils.isEmpty(folderRecords)){
            return Lists.newArrayList();
        }
        Map<Long, BreadCurmbVO> preparedBreadCurmbVoMap = folderRecords.stream().map(BreadCurmbVO::transfer).collect(Collectors.toMap(BreadCurmbVO::getId, v -> v));
        Long fileId = context.getFileId();
        BreadCurmbVO currentNode;
        //这里需要使用双向链表,方便添加头结点
        List<BreadCurmbVO> result = new LinkedList<>();
        
        do{
            currentNode = preparedBreadCurmbVoMap.get(fileId);
            if(Objects.nonNull(currentNode)){
                result.add(0,currentNode);
                fileId = currentNode.getParentId();
            }
        }while (Objects.nonNull(currentNode));
        return result;
    }

    @Override
    public List<RPanUserFile> findAllRecords(List<RPanUserFile> records) {
        List<RPanUserFile> result = Lists.newArrayList(records);
        if(CollectionUtil.isEmpty(result)){
            return result;
        }
        long count = result.stream().filter(record -> Objects.equals(record.getFolderFlag(), FolderFlagEnum.YES.getCode())).count();
        if(count == 0){
            return result;
        }
        //获取用户id
        Long userId = records.get(0).getUserId();
        //查询用户的所有记录
        List<RPanUserFile> list = lambdaQuery().eq(RPanUserFile::getUserId,userId).list();
        List<Long> fileIds = records.stream().map(RPanUserFile::getFileId).collect(Collectors.toList());
        List<RPanUserFile> collect = list.stream().filter(item -> fileIds.contains(item.getFileId())).collect(Collectors.toList());
        //过滤出是文件夹的文件
        collect = collect.stream().filter(item->Objects.equals(item.getFolderFlag(),FolderFlagEnum.YES.getCode())).collect(Collectors.toList());
        collect.stream().forEach(item->getAllRecords(result,item.getFileId(),list));
        return result;
    }

    /**
     * 获取所有子文件夹
     * @param result 结果列表
     * @param parentId 父文件夹Id
     * @param list 文件列表
     */
    private void getAllRecords(List<RPanUserFile> result, Long parentId, List<RPanUserFile> list) {
        if(CollectionUtils.isEmpty(list)){
            return;
        }
        List<RPanUserFile> childRecords = list.stream().filter(item -> item.getParentId().equals(parentId)).collect(Collectors.toList());
        if(CollectionUtils.isEmpty(childRecords)){
            return;
        }
        result.addAll(childRecords);
        //继续过滤出文件夹集合进行递归
        childRecords = childRecords.stream().filter(item->Objects.equals(item.getFolderFlag(),FolderFlagEnum.YES.getCode())).collect(Collectors.toList());
        childRecords.stream().forEach(childRecord->getAllRecords(result,childRecord.getFileId(),list));
    }

    /**
     * 文件搜索之后
     * @param context
     */
    private void afterSearch(FileSearchContext context) {
        UserSearchEvent event = new UserSearchEvent(this,context.getKeyword(),context.getUserId());
        applicationContext.publishEvent(event);
    }


    private List<RPanUserFile> getFileSearchResult(FileSearchContext context) {
        List<Integer> fileTypeArray = context.getFileTypeArray();
        String keyword = context.getKeyword();
        Long userId = context.getUserId();
        List<RPanUserFile> list = lambdaQuery().eq(RPanUserFile::getUserId, userId)
                .in(!CollectionUtils.isEmpty(fileTypeArray),RPanUserFile::getFileType, fileTypeArray)
                .like(StringUtils.isNotBlank(keyword), RPanUserFile::getFilename, keyword)
                .eq(RPanUserFile::getDelFlag, DelFlagEnum.NO.getCode())
                .list();
        return list;
    }

    /**
     * 执行文件复制操作
     * 记得把文件夹里面的子文件夹以及文件也要复制
     * @param context
     */
    private void doCopy(CopyFileContext context) {
        List<RPanUserFile> prepareRecords = context.getPrepareRecords();
        if(!CollectionUtils.isEmpty(prepareRecords)){
            List<RPanUserFile> allRecords = new ArrayList<>();
            prepareRecords.stream().forEach(record->assembleChildRecord(allRecords,record,context.getTargetParentId(),context.getUserId()));
            if(!saveBatch(allRecords)){
                throw new RPanBusinessException("复制文件失败");
            }
        }
    }

    /**
     * 处理子文件夹
     * @param allRecords
     * @param record
     * @param targetParentId
     * @param userId
     */
    private void assembleChildRecord(List<RPanUserFile> allRecords, RPanUserFile record, Long targetParentId, Long userId) {
        Long newFileId = IdUtil.get();
        Long oldFileId = record.getFileId();

        record.setUserId(userId);
        record.setFileId(newFileId);
        record.setCreateUser(userId);
        record.setCreateTime(new Date());
        record.setUpdateTime(new Date());
        record.setParentId(userId);
        record.setUpdateUser(userId);
        record.setParentId(targetParentId);
        handleFileName(record);

        allRecords.add(record);
        //是文件夹,它下面的子文件夹也要复制
        if(checkIsFolder(record)){
            List<RPanUserFile> childRecords = findChildRecords(oldFileId);
            if(CollectionUtils.isEmpty(childRecords)){
               return;
            }
            childRecords.stream().forEach(childRecord->assembleChildRecord(allRecords,childRecord,newFileId,userId));
        }
    }

    /**
     * 查询文件夹的子文件夹
     * @param oldFileId
     * @return
     */
    private List<RPanUserFile> findChildRecords(Long oldFileId) {
        return lambdaQuery().eq(RPanUserFile::getParentId,oldFileId)
                .eq(RPanUserFile::getDelFlag,DelFlagEnum.NO.getCode())
                .list();
    }

    /**
     * 检查是否是文件夹
     * @param record
     * @return
     */
    private boolean checkIsFolder(RPanUserFile record) {;
        return record.getFolderFlag().equals(FolderFlagEnum.YES.getCode());
    }

    /**
     * 检查文件复制条件
     * 目标文件必须是文件夹
     * 选择要转移的文件列表不能含有目标文件夹以及其子文件夹
     * @param context
     */
    private void checkCopyCondition(CopyFileContext context) {
        Long targetParentId = context.getTargetParentId();
        RPanUserFile targetFile = getById(targetParentId);
        if(!checkIsFolder(targetFile)){
            throw new RPanBusinessException("目标文件必须是文件夹");
        }
        List<RPanUserFile> rPanUserFiles = listByIds(context.getFileIdList());
        if(checkIsChildFolder(rPanUserFiles,targetParentId,context.getUserId())){
            throw new RPanBusinessException("选择要复制的文件列表不能含有目标文件夹以及其子文件夹");
        }
        context.setPrepareRecords(rPanUserFiles);
    }

    /**
     * 检查文件转移条件
     * 目标文件必须是文件夹
     * 选择要转移的文件列表不能含有目标文件夹以及其子文件夹
     * @param context
     */
    private void checkTransferCondition(TransferFileContext context) {
        if(!checkIsFolder(getById(context.getTargetParentId()))){
            throw new RPanBusinessException("目标文件必须是文件夹");
        }
        List<RPanUserFile> rPanUserFiles = listByIds(context.getFileIdList());
        if(checkIsChildFolder(rPanUserFiles,context.getTargetParentId(),context.getUserId())){
            throw new RPanBusinessException("选择要转移的文件列表不能含有目标文件夹以及其子文件夹");
        }
        context.setPrepareRecords(rPanUserFiles);
    }

    /**
     * 选择要转移的文件列表不能含有目标文件夹以及其子文件夹
     * @param rPanUserFiles
     * @param targetParentId
     * @param userId
     * @return
     */
    private boolean checkIsChildFolder(List<RPanUserFile> rPanUserFiles, Long targetParentId, Long userId) {
        //过滤出是文件夹的文件列表
        List<RPanUserFile> records = rPanUserFiles.stream().filter(item -> item.getFolderFlag().equals(FolderFlagEnum.YES.getCode())).collect(Collectors.toList());
        if(CollectionUtils.isEmpty(records)){
            //如果没有文件夹直接返回false，不用再接着验证
            return false;
        }
        //获取用户文件夹
        List<RPanUserFile> folderRecords = queryFolderList(userId);
        //根据parentId进行分组
        Map<Long, List<RPanUserFile>> folderRecordMap = folderRecords.stream().collect(Collectors.groupingBy(RPanUserFile::getParentId));
        //不能转移的文件
        List<RPanUserFile> unableTransferRecords = new ArrayList<>();
        unableTransferRecords.addAll(records);
        records.forEach(record->findAllChildFolder(unableTransferRecords,folderRecordMap,record));
        List<Long> unableTransferRecordIds = unableTransferRecords.stream().map(RPanUserFile::getFileId).collect(Collectors.toList());
        return unableTransferRecordIds.contains(targetParentId);
    }

    /**
     * 查找子文件夹
     * @param unableTransferRecords
     * @param folderRecordMap
     * @param record
     */
    private void findAllChildFolder(List<RPanUserFile> unableTransferRecords, Map<Long, List<RPanUserFile>> folderRecordMap, RPanUserFile record) {
        if(Objects.isNull(record)){
            return;
        }
        List<RPanUserFile> childFolderRecords = folderRecordMap.get(record.getFileId());
        if(CollectionUtils.isEmpty(childFolderRecords)){
            return;
        }
        unableTransferRecords.addAll(childFolderRecords);
        childFolderRecords.stream().forEach(child->findAllChildFolder(unableTransferRecords,folderRecordMap,child));
    }


    /**
     * 查询用户的文件夹列表
     * @param userId
     */
    private List<RPanUserFile> queryFolderList(Long userId) {
        List<RPanUserFile> list = lambdaQuery().eq(RPanUserFile::getUserId, userId)
                .eq(RPanUserFile::getFolderFlag, FolderFlagEnum.YES.getCode())
                .eq(RPanUserFile::getDelFlag, DelFlagEnum.NO.getCode())
                .list();
        return list;
    }

    /**
     * 执行移动操作
     * @param context
     */
    private void doTransfer(TransferFileContext context) {
        List<RPanUserFile> prepareRecords = context.getPrepareRecords();
        Long userId = context.getUserId();
        prepareRecords.forEach(record->{
            record.setUserId(userId);
            record.setCreateUser(userId);
            record.setUpdateTime(new Date());
            record.setParentId(userId);
            record.setUpdateUser(userId);
            handleFileName(record);
        });
        if(!updateBatchById(prepareRecords)){
            throw new RPanBusinessException("文件转移失败");
        }

    }

    /**
     * 拼装文件夹树
     * @param record
     * @return
     */
    private List<FolderTreeNodeVO> assembFolderTreeNodeVOList(List<RPanUserFile> record) {
        //批量转换
        List<FolderTreeNodeVO> mappedFolderTreeNodeVOList =  record.stream().map(fileConverter::rPanUserFileToFolderTreeNodeVO).collect(Collectors.toList());
        //根据parentId进行分组
        Map<Long, List<FolderTreeNodeVO>> mappedFolderTreeNodeVOMap = mappedFolderTreeNodeVOList.stream().collect(Collectors.groupingBy(FolderTreeNodeVO::getParentId));
        for (FolderTreeNodeVO folderTreeNodeVO : mappedFolderTreeNodeVOList) {
            Long id = folderTreeNodeVO.getId();
            List<FolderTreeNodeVO> children = mappedFolderTreeNodeVOMap.get(id);
            if(!CollectionUtils.isEmpty(children)){
                folderTreeNodeVO.getChildren().addAll(children);
            }
        }
        //过滤出顶级节点
        mappedFolderTreeNodeVOList = mappedFolderTreeNodeVOList.stream().filter(item->Objects.equals(item.getParentId(),FileConstants.TOP_PARENT_ID)).collect(Collectors.toList());
        return mappedFolderTreeNodeVOList;
    }

    /**
     * 执行文件预览操作
     * 添加跨域的公共响应头
     * 委托文件存储引擎去读取文件内容到响应的输出流中
     * @param record
     * @param response
     */
    private void doPreviewFile(RPanUserFile record, HttpServletResponse response) {
        RPanFile realFileRecord = rPanFileService.getById(record.getRealFileId());
        if(Objects.isNull(realFileRecord)){
            throw new RPanBusinessException("当前的文件记录不存在");
        }
        addCommonResponseHeader(response, realFileRecord.getFilePreviewContentType());
        realFileToOutputStream(realFileRecord.getRealPath(),response);
    }

    /**
     * 执行下载操作
     * 查询文件的真实存储路径
     * 添加跨域的公共响应头
     * 拼装下载文件的名称，长度等响应信息
     * 委托文件存储引擎去读取文件内容到响应的输出流中
     * @param record
     * @param response
     */
    private void doDownloadFile(RPanUserFile record, HttpServletResponse response) {
        RPanFile realFileRecord = rPanFileService.getById(record.getRealFileId());
        if(Objects.isNull(realFileRecord)){
            throw new RPanBusinessException("当前的文件记录不存在");
        }
        addCommonResponseHeader(response, MediaType.APPLICATION_OCTET_STREAM_VALUE);
        addDownloadAttribute(response,record,realFileRecord);
        realFileToOutputStream(realFileRecord.getRealPath(),response);

    }

    /**
     * 委托文件存储引擎去读取文件内容到响应的输出流中
     * @param realPath
     * @param response
     */
    private void realFileToOutputStream(String realPath, HttpServletResponse response){
        try {
            ReadFileContext context = new ReadFileContext();
            context.setRealPath(realPath);
            context.setOutputStream(response.getOutputStream());
            storageEngine.readFile(context);
        } catch (IOException e) {
            e.printStackTrace();
            throw new RPanBusinessException("下载文件失败");
        }
    }

    /**
     * 拼装下载文件的名称，长度等响应信息
     * @param response
     * @param record
     * @param realFileRecord
     */
    private void addDownloadAttribute(HttpServletResponse response, RPanUserFile record, RPanFile realFileRecord) {
        try {
            response.addHeader(FileConstants.CONTENT_DISPOSITION_STR,
                    FileConstants.CONTENT_DISPOSITION_VALUE_PREFIX_STR + new String(record.getFilename().getBytes(FileConstants.GB2312_STR),FileConstants.IOS_8859_1_STR));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            throw new RPanBusinessException("文件下载失败");
        }
        response.setContentLengthLong(Long.valueOf(realFileRecord.getFileSize()));
    }

    /**
     * 添加跨域的公共响应头
     * @param response
     * @param contentTypeValue
     */
    private void addCommonResponseHeader(HttpServletResponse response, String contentTypeValue) {
        response.reset();
        HttpUtil.addCorsResponseHeaders(response);
        response.addHeader(FileConstants.CONTENT_TYPE_STR,contentTypeValue);
        response.setContentType(contentTypeValue);
    }

    /**
     * 检查是否是文件夹
     * @param record
     * @return
     */
    private boolean CheckFolder(RPanUserFile record) {
        return FolderFlagEnum.YES.getCode().equals(record.getFolderFlag());
    }

    /**
     * 检查用户权限
     * @param record
     * @param userId
     */
    private void checkOperatePermission(RPanUserFile record, Long userId) {
        if(Objects.isNull(record)){
            throw new RPanBusinessException("文件信息不存在");
        }
        if(!Objects.equals(record.getCreateUser(),userId)){
            throw new RPanBusinessException("该用户无权限下载文件");
        }
    }

    /**
     * 文件物理合并
     * 保存文件实体记录
     * @param context
     */
    private void mergeFileChunkAndSaveFile(FileChunkMergeContext context) {
        FileChunkMergeAndSaveContext fileChunkMergeAndSaveContext = fileConverter.fileChunkMergeContextToFileChunkMergeAndSaveContext(context);
        rPanFileService.mergeFileChunkAndSaveFile(fileChunkMergeAndSaveContext);
        context.setRecord(fileChunkMergeAndSaveContext.getRecord());
    }

    /**
     * 上传文件并保存文件信息
     * @param fileUploadContext
     * @return
     */
    private void saveFile(FileUploadContext fileUploadContext) {
        FileSaveContext fileSaveContext =  fileConverter.fileUploadCOntextToFileSaveContext(fileUploadContext);
        rPanFileService.saveFile(fileSaveContext);
        fileUploadContext.setRecord(fileSaveContext.getRecord());
    }


    /**
     * 获取文件记录
     * @param userId
     * @param identifier
     * @return
     */
    private RPanFile getRecordByUserIdAndIdentifier(Long userId, String identifier) {
        LambdaQueryWrapper<RPanFile> lqw = new LambdaQueryWrapper<>();
        lqw.eq(RPanFile::getCreateUser,userId).eq(RPanFile::getIdentifier,identifier);
        RPanFile record = rPanFileService.getOne(lqw);
        return record;
    }

    /**
     * 对外发布文章删除的事件
     * @param deleteFileContext
     */
    private void afterDeleteFile(DeleteFileContext deleteFileContext) {
        DeleteFileEvent deleteFileEvent = new DeleteFileEvent(this, deleteFileContext.getFileIdList());
        applicationContext.publishEvent(deleteFileEvent);
    }

    /**
     * 执行删除操作
     * @param deleteFileContext
     */
    private void doDeleteFile(DeleteFileContext deleteFileContext) {
        List<RPanUserFile> fileList = deleteFileContext.getFileList();
        fileList.stream().forEach(item->{
            item.setDelFlag(DelFlagEnum.YES.getCode());
            item.setUpdateTime(new Date());
        });
        if(!updateBatchById(fileList)){
            throw new RPanBusinessException("删除文件失败");
        }
    }

    /**
     * 检查用户删除资格
     * @param deleteFileContext
     */
    private void checkDeleteFile(DeleteFileContext deleteFileContext) {
        List<Long> fileIds = deleteFileContext.getFileIdList();
        List<RPanUserFile> rPanUserFiles = listByIds(fileIds);
        if(CollectionUtils.isEmpty(rPanUserFiles)){
            throw new RPanBusinessException("文件信息不存在");
        }
        if(rPanUserFiles.size() != fileIds.size()){
            throw new RPanBusinessException("存在不合法的文件记录");
        }
        Set<Long> fileIdSet = rPanUserFiles.stream().map(RPanUserFile::getFileId).collect(Collectors.toSet());
        fileIdSet.addAll(fileIds);
        int newSize = fileIdSet.size();
        int oldSize = fileIds.size();
        if(newSize != oldSize){
            throw new RPanBusinessException("存在不合法的文件记录");
        }
        Set<Long> userIdSet = rPanUserFiles.stream().map(RPanUserFile::getUserId).collect(Collectors.toSet());
        if(userIdSet.size() != 1){
            throw new RPanBusinessException("存在不合法的文件记录");
        }
        Long userId = userIdSet.stream().findFirst().get();
        if(!Objects.equals(userId,deleteFileContext.getUserId())){
            throw new RPanBusinessException("当前登录用户没有删除文件的权限");
        }
        deleteFileContext.setFileList(rPanUserFiles);
    }

    /**
     * 修改文件名称操作
     * @param updateFilenameContext
     */
    private void doUpdateFilename(UpdateFilenameContext updateFilenameContext) {
        RPanUserFile entity = updateFilenameContext.getEntity();
        entity.setUpdateTime(new Date());
        entity.setUpdateUser(updateFilenameContext.getUserId());
        entity.setFilename(updateFilenameContext.getNewFilename());
        if(!updateById(entity)){
            throw new RPanBusinessException("修改文件名称失败");
        }

    }

    /**
     * 检查是否可以修改
     * @param updateFilenameContext
     */
    private void checkUpdateFilename(UpdateFilenameContext updateFilenameContext) {
        Long fileId = updateFilenameContext.getFileId();
        RPanUserFile entity = lambdaQuery().eq(RPanUserFile::getFileId, fileId).one();
        if(Objects.isNull(entity)){
            throw new RPanBusinessException("该文件ID无效");
        }
        if(!Objects.equals(entity.getUserId(),updateFilenameContext.getUserId())){
            throw new RPanBusinessException("当前登录用户没有修改该文件名称的权限");
        }
        if(Objects.equals(updateFilenameContext.getNewFilename(),entity.getFilename())){
            throw new RPanBusinessException("新旧名称不能相同");
        }
        //不能与当前文件夹下的子文件同名
        Integer count = getNewFilenameCount(entity,updateFilenameContext);
        if(count > 0){
            throw new RPanBusinessException("不能与当前文件夹下的子文件同名");
        }
        //保存entity
        updateFilenameContext.setEntity(entity);
    }

    /**
     * 查找当前文件夹下与新文件名称相同的文件个数
     * @param entity
     * @param updateFilenameContext
     * @return
     */
    private Integer getNewFilenameCount(RPanUserFile entity,UpdateFilenameContext updateFilenameContext){
        String newFilename = updateFilenameContext.getNewFilename();
        return lambdaQuery()
                .eq(RPanUserFile::getParentId, entity.getParentId())
                .eq(RPanUserFile::getUserId,entity.getUserId())
                .eq(RPanUserFile::getDelFlag,DelFlagEnum.NO.getCode())
                .eq(RPanUserFile::getFilename, newFilename)
                .count();
    }


    /**
     * 保存用户文件信息
     * @param parentId
     * @param fileName
     * @param folderFlagEnum
     * @param fileType
     * @param realFileId
     * @param userId
     * @param fileDesc
     * @return
     */
    private Long saveUserFile(Long parentId,
                              String fileName,
                              FolderFlagEnum folderFlagEnum,
                              Integer fileType,
                              Long realFileId,
                              Long userId,
                              String fileDesc){
       RPanUserFile userFile =  assembleUserFile(parentId,fileName,folderFlagEnum,fileType,realFileId,userId,fileDesc);
       if(!save(userFile)){
           throw new RPanBusinessException("保存用户文件信息失败");
       }
       return userFile.getFileId();
    }

    /**
     * 封装RPanUserFile实体类
     * 处理寄文件名称，
     * @param parentId
     * @param fileName
     * @param folderFlagEnum
     * @param fileType
     * @param realFileId
     * @param userId
     * @param fileDesc
     * @return
     */
    private RPanUserFile assembleUserFile(Long parentId,
                                          String fileName,
                                          FolderFlagEnum folderFlagEnum,
                                          Integer fileType,
                                          Long realFileId,
                                          Long userId,
                                          String fileDesc) {
        RPanUserFile userFile = new RPanUserFile();
        userFile.setFileId(IdUtil.get());
        userFile.setParentId(parentId);
        userFile.setFileType(fileType);
        userFile.setFilename(fileName);
        userFile.setRealFileId(realFileId);
        userFile.setCreateUser(userId);
        userFile.setUserId(userId);
        userFile.setFileSizeDesc(fileDesc);
        userFile.setDelFlag(DelFlagEnum.NO.getCode());
        userFile.setFolderFlag(folderFlagEnum.getCode());
        userFile.setCreateTime(new Date());
        userFile.setUpdateTime(new Date());
        userFile.setUpdateUser(userId);
        //处理文件名称
        handleFileName(userFile);
        return userFile;
    }

    /**
     * 处理同名文件名称
     * 相同的文件：例1.jpg，1（1）.jpg
     * @param userFile
     * @return
     */
    private void handleFileName(RPanUserFile userFile) {
        String fileName = userFile.getFilename(),
                newFileNameWithoutSuffix,
                newFileNameSuffix;
        //获取.的位置
        int filePointPosition = fileName.lastIndexOf(RPanConstants.POINT_STR);
        if(filePointPosition == RPanConstants.MINUS_ONE_INT){
            //没有后缀名
            newFileNameWithoutSuffix = fileName;
            newFileNameSuffix = StringUtils.EMPTY;
        }else{
            //有后缀名
            newFileNameWithoutSuffix = fileName.substring(RPanConstants.ZERO_INT,filePointPosition);
            newFileNameSuffix = fileName.replace(newFileNameWithoutSuffix,StringUtils.EMPTY);
        }
        int count = getFileNameCount(userFile,newFileNameWithoutSuffix);
        if(count > 0){
            String newFileName = createNewFileName(newFileNameSuffix,count,newFileNameWithoutSuffix);
            userFile.setFilename(newFileName);
        }

    }

    /**
     * 创建新文件名
     * @param newFileNameSuffix
     * @param count
     * @param newFileNameWithoutSuffix
     * @return
     */
    private String createNewFileName(String newFileNameSuffix, int count, String newFileNameWithoutSuffix) {
        String newFileName = new StringBuffer().append(newFileNameWithoutSuffix)
                .append(FileConstants.LEFT)
                .append(count)
                .append(FileConstants.RIGHT)
                .append(newFileNameSuffix)
                .toString();
        return newFileName;
    }

    /**
     * 获取文件同名数量
     * @param entity
     * @param newFileNameWithoutSuffix
     * @return
     */
    private int getFileNameCount(RPanUserFile entity,String newFileNameWithoutSuffix) {
       return baseMapper.getFileNameCount(entity,newFileNameWithoutSuffix);
    }
}
